﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using DiscoveryLogic.Analytics.DataObjects;
using DiscoveryLogic.Common.Utilities;
using EntityBase=DiscoveryLogic.Analytics.DataObjects.EntityBase;

namespace DiscoveryLogic.Analytics.BusinessLogic.Entities
{
	public class EntityManager
	{
		public static List<EntityBase> GetEntitiesByType(EntityType entityType)
		{
		    return new List<EntityBase>();
        }

        public static Dictionary<string, List<EntityBase>> GetEntityDataSets(EntityType entityType)
        {
            Dictionary<string, List<EntityBase>> entityDataSets=new Dictionary<string, List<EntityBase>>();
            string appFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
            appFolderPath = Path.Combine(appFolderPath, "Network Data");
            if (!Directory.Exists(appFolderPath))
                Directory.CreateDirectory(appFolderPath);
            DirectoryInfo dirInfo=new DirectoryInfo(appFolderPath);
            string extension = EntityTypeUtil.GetEntityDataSetFileExtension(entityType);
            
            FileInfo[] files = dirInfo.GetFiles("*"+extension, SearchOption.TopDirectoryOnly);
            if(files !=null && files.Length>0)
            {
                foreach(FileInfo file in files)
                {
                    if (!file.Name.ToLower().EndsWith(extension))
                        continue;
                    string dataSetName = Path.GetFileNameWithoutExtension(file.FullName);
                    XmlDocument xDoc = new XmlDocument();
                    xDoc.Load(file.FullName);
                    switch (entityType)
                    {
                        case EntityType.Publications:
                            List<EntityBase> pubs = PublicationDO.ReadPubs(xDoc);
                            entityDataSets.Add(dataSetName, pubs);
                            break;
                        case EntityType.Persons:
                            List<EntityBase> persons = PersonDO.ReadPersons(xDoc);
                            entityDataSets.Add(dataSetName, persons);
                            break;
                        case EntityType.Grants:
                            List<EntityBase> grants = GrantDO.ReadGrants(xDoc);
                            entityDataSets.Add(dataSetName, grants);
                            break;
                        case EntityType.Organizations:
                            List<EntityBase> orgs = ExternalOrgDO.ReadOrgs(xDoc);
                            entityDataSets.Add(dataSetName, orgs);
                            break;
                        case EntityType.Document:
                            List<EntityBase> docs = Document.ReadDocs(xDoc);
                            entityDataSets.Add(dataSetName, docs);
                            break;
                    }
                }
            }

            return entityDataSets;
        }

        public static DataTable ToTable(EntityType entityType, List<EntityBase> entities)
        {
            if(entities ==null || entities.Count==0)
                throw new Exception("Unable to read entities");
            
            PropertyInfo[] props = EntityTypeUtil.GetProperties(entityType);
            DataTable dt = new DataTable(entityType.ToString());
            foreach(PropertyInfo prop in props)
            {
                dt.Columns.Add(prop.Name, prop.PropertyType);
            }
            foreach(EntityBase entity in entities)
            {
                DataRow dr = dt.NewRow();
                foreach(PropertyInfo prop in props)
                {
                    dr[prop.Name] = prop.GetValue(entity, null);
                }
                dt.Rows.Add(dr);
            }
            return dt;
        }

        public static List<EntityBase> ToEntityList(EntityType entityType, SetDataProvider provider,
            Dictionary<string, string> entityFieldTableFieldMappings)
        {
            DataTable dt = provider.GetDataFromProvider();
            return ToEntityList(entityType, dt, entityFieldTableFieldMappings);
        }

        public static List<EntityBase> ToEntityList(EntityType entityType, SetDataProvider provider)
        {
            DataTable dt = provider.GetDataFromProvider();
            return ToEntityList(entityType, dt);
        }

        public static List<EntityBase> ToEntityList(EntityType entityType, DataTable dt)
        {
            if (dt == null)
                throw new Exception("Unable to read data from table");
            List<EntityBase> entities=new List<EntityBase>();
            Type type = EntityTypeUtil.GetType(entityType);
            PropertyInfo[] props = EntityTypeUtil.GetProperties(entityType);
            List<string> keysProcessed=new List<string>();
            foreach(DataRow dr in dt.Rows)
            {
                EntityBase entity = (EntityBase) Activator.CreateInstance(type);
                foreach(PropertyInfo prop in props)
                {
                    if(dt.Columns.Contains(prop.Name))
                    {
                        object fieldValue = dr[prop.Name];
                        if(fieldValue !=null)
                        {
                            SimpleDataType dbType = SimpleDataTypeUtil.ToSimpleDataType(prop.PropertyType);
                            fieldValue = SimpleDataTypeUtil.ReadDbValue(dbType, fieldValue);
                            prop.SetValue(entity, fieldValue,null);
                        }
                    }
                }
                if(entity.PKValue()!=null && !keysProcessed.Contains(entity.PKValue()))
                {
                    keysProcessed.Add(entity.PKValue());
                    entities.Add(entity);
                }
            }
            return entities;
        }

        public static List<EntityBase> ToEntityList(EntityType entityType, DataTable dt, 
            Dictionary<string,string> entityFieldTableFieldMappings)
        {
            if (dt == null)
                throw new Exception("Unable to read data from table");
            List<string> keysProcessed = new List<string>();
            List<EntityBase> entities = new List<EntityBase>();
            Type type = EntityTypeUtil.GetType(entityType);
            PropertyInfo[] props = EntityTypeUtil.GetProperties(entityType);
            foreach (DataRow dr in dt.Rows)
            {
                EntityBase entity = (EntityBase)Activator.CreateInstance(type);
                foreach (PropertyInfo prop in props)
                {
                    if (entityFieldTableFieldMappings.ContainsKey(prop.Name))
                    {
                        string colName = entityFieldTableFieldMappings[prop.Name];
                        if (dt.Columns.Contains(colName))
                        {
                            object fieldValue = dr[colName];
                            
                            if (fieldValue != null && fieldValue != DBNull.Value)
                            {
                                SimpleDataType dbType = SimpleDataTypeUtil.ToSimpleDataType(prop.PropertyType);
                                fieldValue = SimpleDataTypeUtil.ReadDbValue(dbType, fieldValue);
                                prop.SetValue(entity, fieldValue, null);
                            }
                        }
                    }
                }
                if (entity.PKValue() != null && !keysProcessed.Contains(entity.PKValue()))
                {
                    keysProcessed.Add(entity.PKValue());
                    entities.Add(entity);
                }
            }
            return entities;
        }

        #region test
        public static List<PersonDO> GeneratePersonsForTest()
        {
            List<PersonDO> persons = new List<PersonDO>();
            PersonDO person = new PersonDO();
            person.PersonID = 1;
            person.FirstName = "John";
            person.LastName = "Smith";
            person.SSN = "123456789";
            person.Degree = "MD. PhD";
            person.Email = "John.Smith@harvard.edu";
            person.Telephone = "604-202-1200";
            person.DOB = new DateTime(1974, 3, 4);
            person.Title = "Dr.";
            persons.Add(person);

            for (int i = 2; i <= 15; i++)
            {
                person = new PersonDO();
                person.PersonID = i;
                person.FirstName = "David";
                person.LastName = "Walter";
                person.Email = "dwalter1@nih.gov";
                person.Telephone = "301-231-3409";
                person.SSN = "123-45-6789";
                person.Degree = "MS";
                person.DOB = new DateTime(1980, 7, 15);
                person.Title = "Mr.";
                persons.Add(person);
            }

            return persons;
        }

        public static List<GrantDO> GeneratGrantsForTest()
        {
            List<GrantDO> grants = new List<GrantDO>();
            for (int i = 1; i <= 15; i++)
            {
                GrantDO grant=new GrantDO();
                grant.ProjectID = i;
                grant.GrantNumber = "5 R01AI 012345-01";
                grant.Abstract = string.Empty;
                grant.AccessionNum = 12345;
                grant.ActivityCode = "R01";
                grant.ApplTypeCode = 1;
                grant.PhsAdminOrg = "AI";
                grant.SerialNum = 12345;
                grant.Title = "Title";
                grants.Add(grant);
            }
            return grants;
        }

	    public static List<ExternalOrgDO> GeneratOrgsForTest()
	    {
            List<ExternalOrgDO> orgs=new List<ExternalOrgDO>();
            for(int i=1;i<=15;i++)
            {
                ExternalOrgDO org=new ExternalOrgDO();
                org.ExternalOrgID = i;
                org.OrgName = "Org name";
                org.Department = "Dept";
                org.City = "City";
                org.Region = "MD";
                org.Country = "USA";
                org.PostalCode = "12345";
                org.EIN = "0";
                org.DunsID = "1";
                orgs.Add(org);
            }
	        return orgs;
        }
       
	    public static List<PublicationDO> GeneratPubsForTest()
	    {
            List<PublicationDO> pubs=new List<PublicationDO>();
            for(int i=1;i<=15;i++)
            {
                PublicationDO pub=new PublicationDO();
                pub.PMID = i;
                pub.Title = "Title";
                pub.Journal = "journal";
                pub.PubDate = DateTime.Now;
                pub.Affiliation = "School";
                pub.Authors = "Authors";
                pubs.Add(pub);
            }
	        return pubs;
        }

        public static List<EntityDataSet> GenerateDataSetNames(EntityType entityType, string iconPath)
        {
            List<EntityDataSet> dataSets = new List<EntityDataSet>();
            for(int i=1;i<=15;i++)
            {
                EntityDataSet entityDataSet=new EntityDataSet(entityType.ToString(), iconPath);
                entityDataSet.DataSetName = string.Format("Data set {0}", i);
                entityDataSet.Entities = new List<EntityBase>();
                entityDataSet.EntityType = entityType;
                entityDataSet.SourceType = SetDataSourceType.DB;
                dataSets.Add(entityDataSet);
            }
            return dataSets;
        }
        #endregion

    }
}
